from django.db import models, IntegrityError
"""
Models for asc_db app.
"""

SEXES = (
         (1, 'Male'),
         (2, 'Female')
        )


# If these roles are changed, save logic in FamilialRelationship model
# will need to be changed to reflect the changes.
RELATION_TYPES = (
                  (1, 'spouse'),
                  (2, 'parent/child'),
                 )

# If these roles are changed, save logic in FamilialRelationship model
# will need to be changed to reflect the changes.
ROLE_TYPES = (
              (1, 'husband'),
              (2, 'wife'),
              (3, 'mother'),
              (4, 'father'),
              (5, 'child'),
             )


class GetUniqueOrNoneManager(models.Manager):
    """Adds get_unique_or_none method to objects"""

    def get_unique_or_none(self, **kwargs):
        try:
            return self.get(**kwargs)
        except (self.model.DoesNotExist, self.model.MultipleObjectsReturned):
            return None

#------------------------------------------------------------------------------


class GroupOrTeam(models.Model):
    name = models.CharField(max_length=255, unique=True)
    description = models.TextField(max_length=1024, null=True, blank=True)
    objects = GetUniqueOrNoneManager()

    def __unicode__(self):
        return self.name

    class Meta:
        db_table = 'group_or_team'

#------------------------------------------------------------------------------


class Address(models.Model):
    flat_number = models.CharField(max_length=255, null=True, blank=True)
    house_name = models.CharField(max_length=255, null=True, blank=True)
    street_number = models.CharField(max_length=255, null=True, blank=True)
    street = models.CharField(max_length=255)
    district = models.CharField(max_length=255, null=True, blank=True)
    city = models.CharField(max_length=255)
    state_or_province = models.CharField(max_length=255, null=True, blank=True)
    postal_code = models.CharField(max_length=7)
    country = models.CharField(max_length=255)
    phone = models.CharField(max_length=11, null=True, blank=True)
    notes = models.TextField(max_length=1024, null=True, blank=True)
    in_parish = models.BooleanField(default=True)
    objects = GetUniqueOrNoneManager()

    # Override the save method to prevent flat_number, house_name,
    # street_number from all being blank
    def save(self, *args, **kwargs):
        super(Address, self).save(*args, **kwargs)
        if self.flat_number in ['', None] and \
           self.house_name in ['', None] and \
           self.street_number in ['', None]:
            raise IntegrityError('need either a flat/street number or name.')

    def __unicode__(self):
        value_list = [
                       self.flat_number,
                       self.house_name,
                       self.street_number,
                       self.street,
                       self.city,
                       self.district,
                      ]
        value_list = filter(None, value_list)
        return ', '.join(value_list)

    class Meta:
        db_table = 'address'

#------------------------------------------------------------------------------


class Title(models.Model):
    title = models.CharField(max_length=255)
    sex_specific = models.IntegerField(null=True, blank=True, choices=SEXES)

    def __unicode__(self):
        return self.title

    class Meta:
        db_table = 'title'

#------------------------------------------------------------------------------


class Person(models.Model):
    title = models.ForeignKey(Title, null=True, blank=True,
                              db_column='title_id')
    first_name = models.CharField(max_length=255)
    middle_names = models.CharField(max_length=255, null=True, blank=True)
    last_name = models.CharField(max_length=255, null=True, blank=True)
    known_as = models.CharField(max_length=255, null=True, blank=True)
    sex = models.IntegerField(choices=SEXES)
    date_of_birth = models.DateField()
    address = models.ForeignKey(Address, db_column='address_id')
    personal_email = models.EmailField(null=True, blank=True)
    work_email = models.EmailField(null=True, blank=True)
    personal_phone = models.CharField(max_length=11, null=True, blank=True)
    work_phone = models.CharField(max_length=11, null=True, blank=True)
    notes = models.TextField(max_length=1024, null=True, blank=True)
    relations = models.ManyToManyField("self", symmetrical=False,
                                       through='FamilialRelation')
    groups = models.ManyToManyField(GroupOrTeam, through='PersonInGroup')
    date_created = models.DateTimeField(auto_now_add=True)
    date_modified = models.DateTimeField(auto_now=True)
    objects = GetUniqueOrNoneManager()

    # Override the save method to prevent a person being saved with a
    # different sex to their selected title
    def save(self, *args, **kwargs):
        super(Person, self).save(*args, **kwargs)
        if self.title not in ['', None]:
            if self.title.sex_specific != self.sex:
                raise IntegrityError('title does not match sex.')

    def __unicode__(self):
        value_list = [
                       self.first_name,
                       self.middle_names,
                       self.last_name,
                      ]
        value_list = filter(None, value_list)
        return ' '.join(value_list)

    class Meta:
        db_table = 'person'

#------------------------------------------------------------------------------


class FamilialRelation(models.Model):
    person = models.ForeignKey(Person, db_column='person_id',
                               related_name='relation')
    related_person = models.ForeignKey(Person, db_column='related_person_id',
                               related_name='related_person')
    relation_type = models.IntegerField(choices=RELATION_TYPES)
    person_role = models.IntegerField(choices=ROLE_TYPES)
    related_person_role = models.IntegerField(choices=ROLE_TYPES)

    # Override the save method to ensure that no erroneous data can
    # be saved (form logic should prevent this anyway).
    def save(self, *args, **kwargs):
        super(FamilialRelation, self).save(*args, **kwargs)
        if self.relation_type == 1:  # Spousal relationship
            if self.person_role not in [1, 2] and \
               self.related_person_role not in [1, 2]:  # Husband/wife roles
                raise IntegrityError(
                    'invalid relationship roles for this relationship type.')
        if self.relation_type == 2:  # Parent/child relationship
            if self.person_role not in [3, 4, 5] and \
               self.related_person_role not in [3, 4, 5]:  # Parent/child roles
                raise IntegrityError(
                    'invalid relationship roles for this relationship type.')

    def __unicode__(self):
        return "%s, %s of %s" % (
                                 self.person,
                                 self.relation_type,
                                 self.related_person,
                                )

    class Meta:
        db_table = 'familial_relation'
        unique_together = (('person', 'related_person', 'relation_type'),)

#------------------------------------------------------------------------------


class PersonInGroup(models.Model):
    person = models.ForeignKey(Person, db_column='person_id')
    group_or_team = models.ForeignKey(GroupOrTeam, db_column='group_id')

    class Meta:
        db_table = 'person_in_group'

#------------------------------------------------------------------------------


class RemovedPerson(models.Model):
    title = models.ForeignKey(Title, db_column='title_id')
    first_name = models.CharField(max_length=255)
    middle_names = models.CharField(max_length=255, null=True, blank=True)
    last_name = models.CharField(max_length=255, null=True, blank=True)
    sex = models.IntegerField(choices=SEXES)
    date_removed = models.DateTimeField(auto_now_add=True)
    notes = models.TextField(max_length=1024, null=True, blank=True)
    electoral_roll = models.BooleanField()
    address = models.CharField(max_length=1024)
    phone = models.CharField(max_length=11, null=True, blank=True)

    class Meta:
        db_table = 'removed_person'
